using System.Collections;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using UnityEngine;

namespace Nirvana
{
    public class SceneData : IBigWorldClear
    {
        /// <summary>
        /// 场景中的物体
        /// </summary>
        public SerializedGameObject[] Objects;

        // public SerializedLightmap[] LightMaps;
        // public SerializedReflectionProbe[] ReflectionProbes;
        // public SerializedCamera CameraData;
        // public SerializedEnvironment Environment;

        public string[] ResourcePaths;

        // 不需要进行序列化的节点
        private List<SerializedLight> _lights;
        private RuntimeMeshBlock[][] _smallBlocks;
        private Vector3 _smallMeshBlockOffset;

        public List<SerializedLight> Lights => _lights;
        public RuntimeMeshBlock[][] SmallBlocks => _smallBlocks;
        public Vector3 SmallMeshBlockOffset => _smallMeshBlockOffset;

        /// <summary>
        /// mesh半径小于等于2的组，初始化时临时存储
        /// </summary>
        private static List<SerializedGameObject> _smallMeshList = new List<SerializedGameObject>();

        /// <summary>
        /// mesh半径大于2小于等于10的组，初始化时临时存储
        /// </summary>
        private static List<SerializedGameObject> _middleMeshList = new List<SerializedGameObject>();

        /// <summary>
        /// mesh半径大于10的组，初始化时临时存储
        /// </summary>
        private static List<SerializedGameObject> _largeMeshList = new List<SerializedGameObject>();

#if UNITY_EDITOR
        /// <summary>
        /// 序列化地图数据并保存到文件中
        /// </summary>
        /// <param name="filePath"></param>
        public void SerializeToFile(string filePath)
        {
            FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate);
            BinaryWriter writer = new BinaryWriter(fs);

            // === 写数据 === //
            BigWorldHelper.WriteDatas(writer, ref Objects);
            writer.Write(ResourcePaths.Length);
            foreach (var resPath in ResourcePaths)
            {
                writer.Write(resPath);
            }
            // === 写数据 === //

            writer.Flush();
            writer.Close();
            writer.Dispose();
            fs.Close();
            fs.Dispose();
        }
#endif

        /// <summary>
        /// 反序列化地图数据
        /// </summary>
        /// <param name="bytes"></param>
        public void Deserialize(byte[] bytes)
        {
            BinaryReader reader = new BinaryReader(new MemoryStream(bytes));
            DeserializeObjects(reader);
            int count = reader.ReadInt32();
            ResourcePaths = new string[count];
            for (int i = 0; i < count; i++)
            {
                ResourcePaths[i] = reader.ReadString();
            }
        }

        private void DeserializeObjects(BinaryReader reader)
        {
            int count = reader.ReadInt32();
            Objects = new SerializedGameObject[count];
            for (int i = 0; i < count; i++)
            {
                ObjectType type = (ObjectType) reader.ReadInt32();
                SerializedGameObject obj = null;
                switch (type)
                {
                    case ObjectType.Node:
                        obj = new SerializedGameObject();
                        break;
                    case ObjectType.Light:
                        var lightData = new SerializedLight();
                        _lights.Add(lightData);
                        obj = lightData;
                        break;
                    case ObjectType.Mesh:
                        obj = new SerializedMesh();
                        break;
                    default:
                        Debug.LogWarning("Deserialize Object Error");
                        break;
                }

                obj.Type = type;
                obj.Read(reader);
                Objects[i] = obj;

                SplitObjects(obj, _smallMeshList, _middleMeshList, _largeMeshList);
                CollectLights(obj, _lights);
            }

            // 只对小组的mesh进行分块
            FindBlocks(_smallMeshList);
            _smallMeshList.Clear();
            _middleMeshList.Clear();
            _largeMeshList.Clear();
        }

        /// <summary>
        /// 按照mesh半径大小分割组别
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="small"></param>
        /// <param name="middle"></param>
        /// <param name="large"></param>
        private void SplitObjects(SerializedGameObject obj, List<SerializedGameObject> small, List<SerializedGameObject> middle,
            List<SerializedGameObject> large)
        {
            ObjectType type = obj.Type;
            if (type == ObjectType.Mesh || type == ObjectType.GroupMesh)
            {
                float radius = 0f;
                if (obj is SerializedMesh mesh)
                {
                    radius = mesh.Radius;
                }

                if (radius > 10f) // 半径大于10未大组
                {
                    large.Add(obj);
                }
                else if (radius > 2f) // 半径大于2小于等于10为中组
                {
                    middle.Add(obj);
                }
                else // 半径大于0小于等于2为小组
                {
                    small.Add(obj);
                }
            }
        }

        private void CollectLights(SerializedGameObject obj, List<SerializedLight> lights)
        {
            ObjectType type = obj.Type;
            if (type == ObjectType.GroupNode)
            {

            }
        }

        private void FindBlocks(List<SerializedGameObject> list)
        {
            if (list.Count <= 0) return;
            float top = float.MinValue;
            float bottom = float.MaxValue;
            float left = float.MaxValue;
            float right = float.MinValue;
            for (int i = 0; i < list.Count; i++)
            {
                SerializedMesh data = list[i] as SerializedMesh;
                if (data == null) continue;
                Vector3 center = data.Center;
                if (center.z > top) top = center.z;
                if (center.z < bottom) bottom = center.z;
                if (center.x < left) left = center.x;
                if (center.x > right) right = center.x;
            }

            Vector3 offset = new Vector3(left, 0f, bottom);
            float grassBlockRange = BigWorldHelper.MeshBlockRange;
            int max_x = (int) ((top - bottom) / grassBlockRange) + 1;
            int max_y = (int) ((right - left) / grassBlockRange) + 1;
            _smallBlocks = new RuntimeMeshBlock[max_x][];
            for (int i = 0; i < max_x; i++)
            {
                _smallBlocks[i] = new RuntimeMeshBlock[max_y];
            }

            for (int i = 0; i < list.Count; i++)
            {
                SerializedMesh child = list[i] as SerializedMesh;
                if (child == null) continue;
                RuntimeMeshBlock block = FindBlock(child.Center - offset, grassBlockRange, _smallBlocks);
                if (block == null) continue;
                if (block.meshLoadList == null)
                {
                    block.meshLoadList = new List<IBigWorldLoad>();
                }

                block.meshLoadList.Add(child);
            }

            _smallMeshBlockOffset = offset;
        }

        /// <summary>
        /// 计算mesh的位置在哪个块里，没有RuntimeMeshBlock就创建，有就直接返回
        /// </summary>
        /// <param name="vector">Mesh中心位置到所有mesh围成的正方形的左下角的向量</param>
        /// <param name="grassBlockRange">mesh块大小</param>
        /// <param name="blocksArrays">所有的运行时mesh块</param>
        /// <returns></returns>
        RuntimeMeshBlock FindBlock(Vector3 vector, float grassBlockRange, RuntimeMeshBlock[][] blocksArrays)
        {
            // vector.x是离left的距离
            // vector.y是离bottom的距离
            // grassBlockRange是块的大小
            // 相除后的x和z就是块的索引
            Vector3 vector2 = vector / grassBlockRange;
            int x = (int) vector2.x;
            int z = (int) vector2.z;
            if (z >= 0 && z < blocksArrays.Length)
            {
                RuntimeMeshBlock[] blocks = blocksArrays[z];
                if (x >= 0 && x < blocks.Length)
                {
                    RuntimeMeshBlock block = blocks[x];
                    if (block == null)
                    {
                        block = new RuntimeMeshBlock();
                        blocks[x] = block;
                    }

                    return block;
                }
            }

            return null;
        }

        public void Clear()
        {

        }
    }
}